/**
    ******************************************************************************
    * @file    main.c 
    * @author  18408107475@163.com
    * @version v1.0
    * @date    2025/04/05
    * @brief   STM32F103控制SG90舵机演示程序
    * @details 
    * 功能特性：
    * - 使用TIM3 PWM输出控制SG90舵机
    * - 实现0°-180°角度控制
    * - 包含精确延时函数
    * 
    * 硬件连接：
    * - SG90信号线(橙色) -> PB5 (TIM3_CH2)
    * - SG90电源(红色)   -> 5V
    * - SG90地线(棕色)   -> GND
    * 
    * 注意事项：
    * 1. 需确保电源能提供足够电流(至少300mA)
    * 2. PB5需要配置为TIM3_CH2的复用功能
    * 3. 使用部分重映射将TIM3_CH2映射到PB5
    ******************************************************************************
**/

/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f10x.h"  // STM32标准外设库，包含所有外设寄存器定义

/* 函数声明 ------------------------------------------------------------------*/
void Delay_ms(uint32_t xms);  // 毫秒延时函数声明
void Delay_us(uint32_t xus);  // 微秒延时函数声明
void Delay_s(uint32_t xs);    // 秒延时函数声明
void SG90_Init(void);         // TIM3 PWM初始化函数声明

/* 全局变量 ------------------------------------------------------------------*/
// 当前无全局变量

/**
    ******************************************************************************
    * @brief  微秒级延时
    * @param  xus 延时时长，范围：0 ~ 1864134 us
    * @note   使用SysTick定时器，基于9MHz时钟
    * @retval 无
    * @detail 工作原理：
    *   - SysTick为24位递减计数器
    *   - 计算公式：LOAD = (时钟频率/1000000) * xus - 1
    *   - 9MHz时钟时，1us需要9个时钟周期
    * 注意事项：
    *   - 最大延时受24位计数器限制
    *   - 不适用于在中断中调用
    *   - 使用时确保SysTick未被其他功能占用
    ******************************************************************************
*/
void Delay_us(uint32_t xus)
{
    SysTick->CTRL = 0;                      // 关闭SysTick
    SysTick->LOAD = (9 * xus) - 1;          // 设置重装载值(9MHz时钟下)
    SysTick->VAL = 0x00;                    // 清除当前值
    SysTick->CTRL = 0x00000001;             // 启用SysTick（使用STCLK）
    while(!(SysTick->CTRL & 0x00010000));   // 等待COUNTFLAG置位
    SysTick->CTRL = 0;                      // 关闭SysTick
}

/**
    ******************************************************************************
    * @brief  毫秒级延时
    * @param  xms 延时时长，范围：0~1864 ms
    * @note   基于Delay_us实现，1000us=1ms
    * @retval 无
    * 注意事项：
    *   - 实际最大延时受Delay_us限制
    *   - 存在函数调用开销，精度略低于直接使用SysTick
    *   - 适用于一般延时需求
    ******************************************************************************
*/
void Delay_ms(uint32_t xms)
{
    while(xms--)
    {
        Delay_us(1000);  // 累计延时
    }
}

/**
    ******************************************************************************
    * @brief  秒级延时
    * @param  xs 延时时长，范围：0~1 s
    * @note   基于Delay_ms实现，1000ms=1s
    * @retval 无
    * 注意事项：
    *   - 适用于非精确长延时
    *   - 长时间阻塞式延时影响系统实时性
    *   - 需要更精确的长延时建议使用定时器中断
    ******************************************************************************
*/
void Delay_s(uint32_t xs)
{
    while(xs--)
    {
        Delay_ms(1000);  // 累计延时
    }
} 

/**
    ******************************************************************************
    * @brief  SG90舵机PWM初始化
    * @param  无
    * @retval 无
    * @detail 初始化TIM3通道2输出PWM信号，配置如下：
    * - PWM频率: 50Hz (周期20ms)
    * - 脉冲宽度范围: 0.5ms-2.5ms (对应0°-180°)
    * - 使用PB5引脚(TIM3_CH2)
    * 硬件配置流程：
    * 1. 使能TIM3和GPIOB时钟
    * 2. 配置PB5为复用推挽输出
    * 3. 配置TIM3时基单元
    * 4. 配置PWM输出模式
    * 5. 使能定时器
    ******************************************************************************
*/
void SG90_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    /* 1. 使能 TIM3 时钟（APB1 总线） */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    /* 2. 使能 GPIOB 和 AFIO 时钟（APB2 总线） */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

    /* 3. 配置 TIM3 通道2 部分重映射到 PB5 */
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

    /* 4. 配置 PB5 为复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* 5. 配置 TIM3 时基单元 */
    /* 舵机控制信号要求20ms周期(50Hz) */
    TIM_TimeBaseStructure.TIM_Period = 200 - 1;      // ARR = 200-1 (每个计数100us，200*100us=20ms)
    TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1;  // PSC = 7200-1 (72MHz/7200=10kHz)
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;     // 无时钟分割
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    /* 6. 配置 TIM3 通道2 PWM 模式 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;       // PWM模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;     // 高电平有效
    TIM_OCInitStructure.TIM_Pulse = 5;                     // 初始占空比(0.5ms)
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);

    /* 7. 使能 ARR 和 CCR2 预装载 */
    TIM_ARRPreloadConfig(TIM3, ENABLE);            // 使能自动重装载预装载
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能 CCR2 预装载

    /* 8. 启动 TIM3 */
    TIM_Cmd(TIM3, ENABLE);
}

/**
    ******************************************************************************
    * @brief  主函数
    * @param  无
    * @retval int 程序退出状态(实际不会退出)
    * @detail 程序主流程：
    * 1. 初始化PWM定时器
    * 2. 进入无限循环，控制舵机在0°-90°之间摆动
    *   - 每500ms改变一次角度
    *   - 0°(0.5ms) -> 45°(1ms) -> 90°(1.5ms) -> 135°(2ms) -> 180°(2.5ms)
    *   - 然后反向运动
    ******************************************************************************
*/
int main(void)
{
    /* 初始化TIM3 PWM功能 */
    SG90_Init();
    
    /* 主循环 */
    while(1)
    {
        // 从0°逐步增加到180°
        for(uint32_t i=1; i<=5; i++){
            TIM3->CCR2 = (5 * i);  // 设置PWM脉冲宽度(5=0.5ms, 25=2.5ms)
            Delay_ms(500);         // 保持500ms
            
            // 当到达180°时反向运动
            while(i==5){
                for(uint32_t j=5; j>=1; j--){
                    TIM3->CCR2 = (5 * j);  // 减小PWM脉冲宽度
                    Delay_ms(500);
                }
                i = 1;  // 重置循环变量
            }
        }
    }
    
    // 程序理论上不会执行到这里
    return 0;
}

/*************************************END OF FILE**************************************/